﻿using System;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Client.Common;
using Client.DAL;
using Client.Events;

using Models;

using Newtonsoft.Json;

using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Regions;

namespace Client.ViewModels
{
    public class ChargeBillViewModel : BindableBase, IRegionMemberLifetime
    {
        #region 绑定属性

        //有效列表
        private ObservableCollection<BillEntryModel> _validList;

        public ObservableCollection<BillEntryModel> ValidList
        {
            get { return _validList; }
            set { SetProperty(ref _validList, value, nameof(ValidList)); }
        }

        //选择的有效行
        private object _selectedValid;

        public object SelectedValid
        {
            get { return _selectedValid; }
            set { SetProperty(ref _selectedValid, value, nameof(SelectedValid)); }
        }

        //无效列表
        private ObservableCollection<BillEntryModel> _invalidList;

        public ObservableCollection<BillEntryModel> InvalidList
        {
            get { return _invalidList; }
            set { SetProperty(ref _invalidList, value, nameof(InvalidList)); }
        }

        //当前选择的客户
        private CustomModel _curCust;

        public CustomModel CurCust
        {
            get { return _curCust; }
            set { SetProperty(ref _curCust, value, nameof(CurCust)); }
        }

        //运行中
        private Visibility _running;

        public Visibility Running
        {
            get { return _running; }
            set { SetProperty(ref _running, value, nameof(Running)); }
        }

        //运行中转圈图状态
        private bool _runPic;

        public bool RunPic
        {
            get { return _runPic; }
            set { SetProperty(ref _runPic, value, nameof(RunPic)); }
        }

        #endregion 绑定属性

        #region 属性

        private IEventAggregator _ea;

        #endregion 属性

        #region 命令

        public DelegateCommand NewBillCommand { get; private set; }
        public DelegateCommand SaveBillCommand { get; private set; }
        public DelegateCommand OpenScanCommand { get; private set; }
        public DelegateCommand OpenSelCustCommand { get; private set; }
        public DelegateCommand RemoveValidCommand { get; private set; }

        public bool KeepAlive => false;

        #endregion 命令

        #region 构造函数

        public ChargeBillViewModel(IEventAggregator ea)
        {
            _ea = ea;
            RunPic = false;
            Running = Visibility.Hidden;
            ValidList = new ObservableCollection<BillEntryModel>();
            InvalidList = new ObservableCollection<BillEntryModel>();
            CurCust = new CustomModel();
            //订阅事件
            _ea?.GetEvent<SelCustEvent>().Subscribe(ProcessCustSelect);
            _ea?.GetEvent<ScanCodeEvent>().Subscribe(ProcessCode);
            //命令
            OpenSelCustCommand = new DelegateCommand(OpenCustSelect);
            OpenScanCommand = new DelegateCommand(OpenScan);
            NewBillCommand = new DelegateCommand(NewBill);
            SaveBillCommand = new DelegateCommand(SaveBill);
            RemoveValidCommand = new DelegateCommand(RemoveValid);
        }

        #endregion 构造函数

        #region 方法

        #region 打开客户选择窗口

        private void OpenCustSelect()
        {
            Window w = new Views.CustSelectView()
            {
                Owner = Comm.Shell,
                Title = "选择客户"
            };
            w.ShowDialog();
        }

        #endregion 打开客户选择窗口

        #region 打开扫码窗口

        private bool scanning = false;

        private void OpenScan()
        {
            Window w = new Views.ScanCodeView()
            {
                Owner = Comm.Shell,
                Title = "请扫码"
            };
            scanning = true;
            Task.Run(RunCode);
            w.ShowDialog();
            scanning = false;
        }

        #endregion 打开扫码窗口

        #region 处理扫码

        private ConcurrentQueue<string> _codeQueue = new ConcurrentQueue<string>();

        private void ProcessCode(string code)
        {
            if (string.IsNullOrEmpty(code) || string.IsNullOrWhiteSpace(code))
                return;
            _codeQueue.Enqueue(code);
        }

        private async void RunCode()
        {
            while (scanning || !_codeQueue.IsEmpty)
            {
                if (!_codeQueue.TryDequeue(out string code))
                {
                    System.Threading.Thread.Sleep(100);
                    continue;
                }
                try
                {
                    StateModel procCodeState = new StateModel { CurStep = Steps.Send };
                    void ProcCodeCallback(RetModel rm)
                    {
                        procCodeState.RM = rm;
                        procCodeState.RM.Data = rm.Success ? rm.Data is null ? null : JsonConvert.DeserializeObject<BillEntryModel>(rm.Data?.ToString()) : null;
                        procCodeState.CurStep = rm.Success ? Steps.Succ : Steps.Fail;
                    }
                    BillSrv.GetCodeInfo(ProcCodeCallback, code);
                    await procCodeState.CheckState(10);
                    BillEntryModel b = GenCode(code, procCodeState);
                    App.Current.Dispatcher.Invoke(() =>
                    {
                        if (!string.IsNullOrEmpty(b.ErrReason))
                            InvalidList.Add(b);
                        else
                        {
                            b.ChargeMakerId = b.ChargeMakerId == 0 ? Comm.UserInfo.Id : b.ChargeMakerId;
                            b.ChargeAmount = b.ChargeAmount == 0 ? b.ProDefaultPrice : b.ChargeAmount;
                            ValidList.Add(b);
                        }
                    });
                }
                catch (Exception)
                {
                }
            }
        }

        private BillEntryModel GenCode(string code, StateModel procCodeState)
        {
            BillEntryModel b;
            if (procCodeState.CurStep == Steps.Succ)
            {
                if (procCodeState.RM.Data is BillEntryModel bm)
                    b = bm;
                else
                    b = new BillEntryModel { ErrReason = "未找到指定编号", CodeFull = code };
            }
            else
            {
                b = new BillEntryModel { ErrReason = procCodeState.ErrStr, CodeFull = code };
            }
            b = CheckErr(b, code);
            return b;
        }

        private BillEntryModel CheckErr(BillEntryModel b, string code)
        {
            b.ErrReason = string.IsNullOrEmpty(b.ErrReason) ?
            b.ChargeFlag != 0 ? "此编号已经兑换过" :
            b.SaleFlag == 0 ? "此编号还未出库" :
            ValidList.Where(p => p.CodeFull == b.CodeFull).Any() ? "重复扫描" :
            null :
            b.ErrReason;
            return b;
        }

        #endregion 处理扫码

        #region 处理客户选择

        private void ProcessCustSelect(CustomModel cm)
        {
            if (null == cm)
                return;
            CurCust = cm;
        }

        #endregion 处理客户选择

        #region 移除选择的有效行

        private void RemoveValid()
        {
            if (SelectedValid is null)
            {
                Comm.ShowErr("必须选择一个要移除的项目", "移除项目");
                return;
            }
            if (!(SelectedValid is BillEntryModel bem))
            {
                Comm.ShowErr("选择的项目不正确！", "移除项目");
                return;
            }
            ValidList.Remove(bem);
        }

        #endregion 移除选择的有效行

        #region 新建单据

        private void NewBill()
        {
            CurCust = new CustomModel();
            Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                ValidList?.Clear();
                InvalidList?.Clear();
            }));
        }

        #endregion 新建单据

        #region 保存单据

        private StateModel saveState;

        private async void SaveBill()
        {
            Running = Visibility.Visible;
            RunPic = true;
            try
            {
                saveState = new StateModel { CurStep = Steps.Send };
                CheckSave();
                BillSrv.AddChargeBill(SaveCallback, CurCust, ValidList.ToList());
                await saveState.CheckState(10).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Comm.ShowErr(ex.Message, "保存兑换");
                Running = Visibility.Hidden;
                RunPic = false;
                return;
            }
            if (saveState.CurStep == Steps.Succ)
                NewBill();
            else
            {
                Comm.ShowErr(saveState.ErrStr, "保存兑换");
            }
            Running = Visibility.Hidden;
            RunPic = false;
        }

        private void CheckSave()
        {
            if (ValidList.Count < 1)
            {
                throw new Exception(Properties.Resources.NoDataSave);
            }
            if (CurCust is null)
            {
                throw new Exception(Properties.Resources.NeedSelectCustom);
            }
            if (CurCust?.Id <= 0)
            {
                throw new Exception(Properties.Resources.NeedSelectCustom);
            }
        }

        private void SaveCallback(RetModel rm)
        {
            if (saveState.CurStep != Steps.Send) return;
            saveState.RM = rm;
            saveState.CurStep = rm.Success ? Steps.Succ : Steps.Fail;
        }

        #endregion 保存单据

        #endregion 方法
    }
}